package com.example.makerspace.manager;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.example.makerspace.dao.ApplicationDAO;
import com.example.makerspace.domain.ApplicationDO;
import com.example.makerspace.domain.UserDO;
import com.example.makerspace.dto.*;
import com.example.makerspace.enums.ResultCodeEnum;
import com.example.makerspace.model.ApplicationStatus;
import com.example.makerspace.model.FailMsg;
import com.example.makerspace.utils.DateUtils;
import com.example.makerspace.utils.FileUtils;
import com.example.makerspace.utils.JwtUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

import static com.example.makerspace.enums.ApplicationStatusEnum.*;
import static com.example.makerspace.service.impl.AdminServiceImpl.managerMap;

@Component
public class ApplicationManager extends BaseManager<ApplicationDO, ApplicationDTO>{
    private final static String RESOURCE_PATH_PREFIX = "resource/";

    private final static List<Map<String, String>> FUZZY_QUERY_PARAMS = Lists.newArrayList(
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "username").put(QUERY_TYPE, QUERY_TYPE_TERM).build(),
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "project_name").put(QUERY_TYPE, QUERY_TYPE_MATCH_PHRASE).build(),
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "team_city").put(QUERY_TYPE, QUERY_TYPE_MATCH_PHRASE).build(),
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "team_name").put(QUERY_TYPE, QUERY_TYPE_MATCH_PHRASE).build(),
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "intention_region").put(QUERY_TYPE, QUERY_TYPE_MATCH_PHRASE).build(),
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "project_type").put(QUERY_TYPE, QUERY_TYPE_MATCH_PHRASE).build(),
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "phone").put(QUERY_TYPE, QUERY_TYPE_TERM).build(),
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "team_introduction").put(QUERY_TYPE, QUERY_TYPE_MATCH_PHRASE).build(),
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "project_overview").put(QUERY_TYPE, QUERY_TYPE_MATCH_PHRASE).build(),
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "landing_demand").put(QUERY_TYPE, QUERY_TYPE_MATCH_PHRASE).build(),
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "cooperation_intention").put(QUERY_TYPE, QUERY_TYPE_MATCH_PHRASE).build(),
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "finance_situation").put(QUERY_TYPE, QUERY_TYPE_MATCH_PHRASE).build(),
            ImmutableMap.<String, String>builder().put(QUERY_FIELD, "finance_demand").put(QUERY_TYPE, QUERY_TYPE_MATCH_PHRASE).build()
    );

    @Resource
    private ApplicationDAO applicationDAO;

    @Resource
    private DataManager dataManager;

    private final static String ATTACHMENT_PATH_PREFIX = "attachment/";

    @PostConstruct
    public void init(){
        this.baseDAO = applicationDAO;
        managerMap.put("application", this);
    }

    public final static Map<String, String> APPLICATION_STATUS_MAP = ImmutableMap.<String, String>builder()
            .put(REVIEWING.getCode(), REVIEWING.getDesc())
            .put(APPLICATION_SUCCESS.getCode(), APPLICATION_SUCCESS.getDesc())
            .put(APPLICATION_FAIL.getCode(), APPLICATION_FAIL.getDesc())
            .build();

    public final static Set<String> FAIL_STATUS = ImmutableSet.<String>builder()
            .add("-1")
            .add("-2")
            .add("-3")
            .add("-4")
            .build();

    public final static Set<String> SUCCESS_STATUS = ImmutableSet.<String>builder()
            .add("1")
            .add("2")
            .add("3")
            .add("4")
            .build();

    public Result addApplication(ApplicationDTO applicationDTO) {
        String username = applicationDTO.getUsername();
        ApplicationDO oldApplicationDO = applicationDAO.selectByUsername(username);
        if(oldApplicationDO != null){
            return Result.error(ResultCodeEnum.ALREADY_APPLICATION);
        }
        String absolutePath = FileUtils.saveMultipartFile(applicationDTO.getAttachments(), FileUtils.staticPath + ATTACHMENT_PATH_PREFIX + applicationDTO.getUsername());
        ApplicationDO applicationDO = new ApplicationDO();
        BeanUtils.copyProperties(applicationDTO, applicationDO);
        applicationDO.setAttachments(absolutePath);
        applicationDO.setStatus(REVIEWING.getCode());
        dataManager.addApplication(applicationDTO);
        return addDO(applicationDO, ResultCodeEnum.USER_APPLICATION_SUCCESS);
    }

    public Result checkApplication(HttpServletRequest request) {
        String username = JwtUtils.getUsername(request);
        ApplicationDO applicationDO = applicationDAO.selectByUsername(username);
        if(applicationDO == null){
            return Result.success();
        }
        ApplicationDTO applicationDTO = convert2DTO(applicationDO);
        return Result.success(ResultCodeEnum.ALREADY_APPLICATION, applicationDTO);
    }


    @Override
    public ApplicationDTO convert2DTO(ApplicationDO applicationDO){
        ApplicationDTO applicationDTO = super.convert2DTO(applicationDO);
        String briefPath = FileUtils.getBriefPath(applicationDO.getAttachments());
        applicationDTO.setFilePath(briefPath);
        String status = APPLICATION_STATUS_MAP.get(applicationDO.getStatus());
        applicationDTO.setStatus(status);
        applicationDTO.setFail(FAIL_STATUS.contains(applicationDO.getStatus()));
        return applicationDTO;
    }

    @Override
    public Result deleteById(int id){
        ApplicationDO applicationDO = applicationDAO.selectById(id);
        dataManager.deleteApplication(applicationDO);
        return super.deleteById(id);
    }


    public Result updateApplication(ApplicationDTO applicationDTO) {
        int id = applicationDTO.getId();
        ApplicationDO applicationDO = applicationDAO.selectById(id);
        if(applicationDO == null){
            return Result.error(ResultCodeEnum.APPLICATION_NOT_EXIST);
        }
        dataManager.updateApplication(applicationDTO, applicationDO);
        Optional.ofNullable(applicationDTO.getUsername()).ifPresent(applicationDO::setUsername);
        Optional.ofNullable(applicationDTO.getProjectName()).ifPresent(applicationDO::setProjectName);
        Optional.ofNullable(applicationDTO.getProjectPeriod()).ifPresent(applicationDO::setProjectPeriod);
        Optional.ofNullable(applicationDTO.getTeamCity()).ifPresent(applicationDO::setTeamCity);
        Optional.ofNullable(applicationDTO.getTeamName()).ifPresent(applicationDO::setTeamName);
        Optional.ofNullable(applicationDTO.getCompanyRegistrationTime()).ifPresent(applicationDO::setCompanyRegistrationTime);
        Optional.ofNullable(applicationDTO.getIntentionRegion()).ifPresent(applicationDO::setIntentionRegion);
        Optional.ofNullable(applicationDTO.getProjectType()).ifPresent(applicationDO::setProjectType);
        Optional.ofNullable(applicationDTO.getPhone()).ifPresent(applicationDO::setPhone);
        Optional.ofNullable(applicationDTO.getTeamIntroduction()).ifPresent(applicationDO::setTeamIntroduction);
        Optional.ofNullable(applicationDTO.getProjectOverview()).ifPresent(applicationDO::setProjectOverview);
        Optional.ofNullable(applicationDTO.getLandingDemand()).ifPresent(applicationDO::setLandingDemand);
        Optional.ofNullable(applicationDTO.getCooperationIntention()).ifPresent(applicationDO::setCooperationIntention);
        Optional.ofNullable(applicationDTO.getFinanceSituation()).ifPresent(applicationDO::setFinanceSituation);
        Optional.ofNullable(applicationDTO.getFinanceDemand()).ifPresent(applicationDO::setFinanceDemand);
        Optional.ofNullable(applicationDTO.getAttachments()).ifPresent(attachment->{
            FileUtils.deleteFile(applicationDO.getAttachments());
            String absolutePath = FileUtils.saveMultipartFile(attachment, FileUtils.staticPath + ATTACHMENT_PATH_PREFIX + applicationDTO.getUsername());
            applicationDO.setAttachments(absolutePath);
        });
        Optional.ofNullable(applicationDTO.getStatus()).ifPresent(status -> {
            List<ApplicationStatus> statusLine = applicationDO.getStatusLine();
            if(CollectionUtils.isEmpty(statusLine)){
                statusLine = Lists.newLinkedList();
            }
            List<String> statusList = statusLine.stream().map(ApplicationStatus::getStatusCode).distinct().collect(Collectors.toList());
//            if(CollectionUtils.isNotEmpty(statusLine)){
//                ApplicationStatus applicationStatus = statusLine.get(statusLine.size() - 1);
//                if(FAIL_STATUS.contains(applicationStatus.getStatusCode())){
//                    statusLine.remove(statusLine.size() - 1);
//                }
//            }
            if(!statusList.contains(status)){
                ApplicationStatus applicationStatus = new ApplicationStatus();
                applicationStatus.setStatus(APPLICATION_STATUS_MAP.get(status));
                applicationStatus.setStatusCode(status);
                applicationStatus.setTime(DateUtils.format(new Date(System.currentTimeMillis()), DateUtils.YYYY_MM_DD));
                statusLine.add(applicationStatus);
                applicationDO.setStatusLine(statusLine);
                applicationDO.setStatus(status);
                if(FAIL_STATUS.contains(status)){
                    List<FailMsg> failHistory = Optional.ofNullable(applicationDO.getFailHistory()).orElse(Lists.newArrayList());
                    FailMsg failMsg = new FailMsg();
                    failMsg.setMessage(applicationDTO.getMsg());
                    failMsg.setStatus(APPLICATION_STATUS_MAP.get(status));
                    failMsg.setStatusCode(status);
                    failMsg.setTime(DateUtils.format(new Date(System.currentTimeMillis()), DateUtils.YYYY_MM_DD));
                    failHistory.add(failMsg);
                    applicationDO.setFailHistory(failHistory);
                }
            }
        });
        Optional.ofNullable(applicationDTO.getMsg()).ifPresent(applicationDO::setMsg);
        return updateDO(applicationDO);
    }


    public Result deleteAttachment(ApplicationDTO applicationDTO) {
        ApplicationDO applicationDO = applicationDAO.selectById(applicationDTO.getId());
        if(applicationDO == null){
            return Result.error(ResultCodeEnum.ID_NOT_EXIST);
        }
        String attachmentsPath = applicationDO.getAttachments();
        if(StringUtils.isBlank(attachmentsPath)){
            return Result.error(ResultCodeEnum.ATTACHMENTS_NOT_EXIST);
        }
        FileUtils.deleteFile(attachmentsPath);
        applicationDO.setAttachments("");
        updateDO(applicationDO);
        return Result.success();
    }

    @Override
    public PageResult list(PageRequest pageRequest){
        Map<String, Object> params = pageRequest.getParams();
        Pagination pagination = pageRequest.getPagination();
        String fuzzy = MapUtils.getString(params, "fuzzy");
        if(StringUtils.isBlank(fuzzy)){
            return super.list(pageRequest);
        }
        return search(null, null, FUZZY_QUERY_PARAMS, fuzzy, pagination, ApplicationDO.class);
    }


    public Result save(ApplicationDTO applicationDTO) {
        int id = applicationDTO.getId();
        ApplicationDO applicationDO = applicationDAO.selectById(id);
        if(applicationDO == null){
            return Result.error(ResultCodeEnum.APPLICATION_NOT_EXIST);
        }
        if(!FAIL_STATUS.contains(applicationDO.getStatus())){
            return Result.error(ResultCodeEnum.NOT_ALLOWED_STATUS);
        }
        dataManager.updateApplication(applicationDTO, applicationDO);
        Optional.ofNullable(applicationDTO.getUsername()).ifPresent(applicationDO::setUsername);
        Optional.ofNullable(applicationDTO.getProjectName()).ifPresent(applicationDO::setProjectName);
        Optional.ofNullable(applicationDTO.getProjectPeriod()).ifPresent(applicationDO::setProjectPeriod);
        Optional.ofNullable(applicationDTO.getTeamCity()).ifPresent(applicationDO::setTeamCity);
        Optional.ofNullable(applicationDTO.getTeamName()).ifPresent(applicationDO::setTeamName);
        Optional.ofNullable(applicationDTO.getCompanyRegistrationTime()).ifPresent(applicationDO::setCompanyRegistrationTime);
        Optional.ofNullable(applicationDTO.getIntentionRegion()).ifPresent(applicationDO::setIntentionRegion);
        Optional.ofNullable(applicationDTO.getProjectType()).ifPresent(applicationDO::setProjectType);
        Optional.ofNullable(applicationDTO.getPhone()).ifPresent(applicationDO::setPhone);
        Optional.ofNullable(applicationDTO.getTeamIntroduction()).ifPresent(applicationDO::setTeamIntroduction);
        Optional.ofNullable(applicationDTO.getProjectOverview()).ifPresent(applicationDO::setProjectOverview);
        Optional.ofNullable(applicationDTO.getLandingDemand()).ifPresent(applicationDO::setLandingDemand);
        Optional.ofNullable(applicationDTO.getCooperationIntention()).ifPresent(applicationDO::setCooperationIntention);
        Optional.ofNullable(applicationDTO.getFinanceSituation()).ifPresent(applicationDO::setFinanceSituation);
        Optional.ofNullable(applicationDTO.getFinanceDemand()).ifPresent(applicationDO::setFinanceDemand);
        Optional.ofNullable(applicationDTO.getAttachments()).ifPresent(attachment->{
            if(Boolean.TRUE.equals(applicationDTO.getFlag())){
                FileUtils.deleteFile(applicationDO.getAttachments());
                String absolutePath = FileUtils.saveMultipartFile(attachment, FileUtils.staticPath  + ATTACHMENT_PATH_PREFIX + applicationDTO.getUsername());
                applicationDO.setAttachments(absolutePath);
            }
        });
        return updateDO(applicationDO);
    }

    public Result submit(ApplicationDTO applicationDTO) {
        int id = applicationDTO.getId();
        ApplicationDO applicationDO = applicationDAO.selectById(id);
        if(applicationDO == null){
            return Result.error(ResultCodeEnum.APPLICATION_NOT_EXIST);
        }
        if(!FAIL_STATUS.contains(applicationDO.getStatus())){
            return Result.error(ResultCodeEnum.NOT_ALLOWED_STATUS);
        }
        applicationDO.setStatusLine(Lists.newLinkedList());
        applicationDO.setStatus(REVIEWING.getCode());
        applicationDO.setMsg("");
        return updateDO(applicationDO);
    }

    public void deleteByUser(UserDO userDO) {
        ApplicationDO applicationDO = applicationDAO.selectByUsername(userDO.getUsername());
        if(applicationDO == null){
            return;
        }
        applicationDAO.deleteById(applicationDO.getId());
    }
}
